Public Module modGlobals

#Region "Global Constants"
    '.NES file addresses
    '*******************
    'Address of row 1 screen large tile backgound
    Public gacintLevelLargeTileDataHeaderAddress() As Integer = {&H105A5, &H11278, &H145A5, &H11F8F, &H12C40, &H15500}
    'Address for the beginning of the sprite spawn info
    Public gacintLevelObjectDataHeaderAddress() As Integer = {&H1861C, &H18969, &H1908C, &H1A179, &H1AA84, &H1B154}
    'Address for the ending of the sprite spawn info
    'Public gacintLevelObjectDataEndAddress() As Integer = {&H186FF, &H18AAC, &H19218, &H1A28A, &H1AC16, &H1B30C}
    'Address for the position in memory containing the address for the screen's sprite spawn info
    '(addresses are contained here the defined the beginning of each screen's spawn info - 12 for levels 1-5 and 14 for level 6)
    Public gacintLevelScreenHeaderStartAddress() As Integer = {&H18604, &H18951, &H19074, &H1A161, &H1AA6C, &H1B138}
    'Address array for each level pointing to addresses that contain the pattern table data (The raw data that's sent to the PPU from 0000-1FFF)
    Public gacintLevelPatternTableAddress() As Integer = {&H1C499, &H1C4A9, &H1C4B9, &H1C4C6, &H1C4D6, &H1C4E6}
    'Addresses that define the offset in the PPU Pattern Table (Comes from D00C in the ROM)
    Public gacintLevelLargeTileDefinitionAddress() As Integer = {&H10625, &H112F8, &H14625, &H1200F, &H12CC0, &H15580}
    'Addresses used for loading extra Boss BG tiles.  Triggered by the F2 object
    Public gacintLevelF2LargeTileDefinitionAddress() As Integer = {0, 0, &HAB97, 0, &HAEBE, &HB4EC}   'Level 1,2, and 4 don't use additional BG boss tiles
    'Addresses that contain a value that defines whether a level uses any level 1 graphics.  If non zero, this number defines the first N tiles of Level 1 used.
    Public gacintLevelLargeTileOffsetAddress() As Integer = {&H1CFFE, &H1CFFF, &H1D000, &H1D001, &H1D002, &H1D003}
    'The number of different Large tiles in each level (0 based)
    Public gacintLevelTileCount() As Integer = {&H65, &H79, &H7C, &H73, &H7B, &H86}
    'Addresses that contain the initial palette data for each level (BG and Sprite)
    Public gacintLevelPaletteAddress() As Integer = {&H1D63C, &H1D65F, &H1D682, &H1D6A5, &H1D6C8, &H1D6EB}
    'Addresses that contain the palette choice in 16x16 sections (4 total) for each 32x32 tile (1 byte per 4x4 tile)
    Public gacintLevelLargeTilePaletteAddress() As Integer = {&H10C85, &H11988, &H14DF5, &H1263F, &H13370, &H15DF0}
    'Contains the addressess defining the palette information for the flashing helipad lights
    Public gacintLevelHelipadPaletteAddress() As Integer = {&H1D70E, &H1D747, &H1D780, &H1D7B9, &H1D7F2, &H1D82B}
    'Contains the addressess defining the palette information for the flashing helipad lights
    Public gacintLevelHelipadPaletteHACKEDAddress() As Integer = {&H1D70E, &H1D723, &H1D738, &H1D74D, &H1D762, &H1D777}
    'Defines the header address for the F2 BG data (16 bytes)
    Public gacintLevelF2BGpaletteAddress() As Integer = {0, 0, &H1D877, 0, &H1D88A, &H1D8AD}
    'Defines the header address for the F2 Sprite data (16 bytes)
    Public gacintLevelF2SpritePaletteAddress() As Integer = {&H1D864, 0, 0, 0, &H1D89A, 0}
    'This defines when to switch to the helipad palette and when to switch back to the normal palette
    '(byte1= switch to heli palette, byte2 = switch back to normal palette)
    Public gacintLevelHelipadScreenAddress() As Integer = {&H1DE2B, &H1DE2D, &H1DE2F, &H1DE31, &H1DE33, &H1DE35}

    'The pale salmon color that is the background for the objects.  This is made transparent when the objects are drawn on the level
    Public gcclrObjectBackground = Color.FromArgb(255, 255, 174, 201)
#End Region

    Public gintLevel As Integer 'The level (radio button) chosen by the user

    'Contains all the enemies and objects that spawn in each level.
    ''These are called object becuase not all have visible apsects (i.e. the gate, boss control objects, escalators in Level 6, etc.)
    Public gaObjects() As LevelObjectStruct

#Region "Structures"
    Public Structure LevelStruct
        Dim LevelData() As Byte
    End Structure

    Public Structure PaletteStruct
        Dim PaletteData() As Byte
    End Structure

    Public Structure HelipadStruct
        Dim PaletteData() As Byte
        Dim StartScreen As Byte 'The screen where the helipad palette (flashed 3 different palettes) is used to overwrite the existing BG palette
        Dim EndScreen As Byte   'The screen where the normal BG palette is put back in place
    End Structure

    'The F2 object can potentially overwrite BG and/or sprite palettes
    Public Structure F2PaletteStruct
        Dim BGPaletteData() As Byte
        Dim SpritePaletteData() As Byte
    End Structure

    Public Structure PatternTableStruct
        Dim PatternTableData() As Byte
    End Structure

    'Some call these events.
    Public Structure ObjectStruct
        Dim Y As Integer
        Dim X As Integer
        Dim ObjectValue As Byte
        Dim SpawnAtBottom As Boolean
    End Structure

    Public Structure LevelObjectStruct
        Dim LevelObjects() As ObjectStruct
        Dim ConversionTable() As ConversionTableStruct
        Dim Offset() As Point
    End Structure

    Public Structure ConversionTableStruct
        Dim Name As String
        Dim ObjectValue As Integer
    End Structure
#End Region

    ''' <summary>
    ''' Decompresses the Konami RLE data.
    ''' </summary>
    ''' <param name="pabytCompressed">The data stream to decompress</param>
    ''' <returns></returns>
    ''' <remarks>Algorithm is as follows: each section of data has a header.  
    ''' if the header's MSB is 1, then the next n bytes are read normally. if the header's MSB is 0, the next byte is read n times.  
    ''' n = header AND 7F.  FF marks the end of the data stream.  After a data section is complete, the next byte is the next header</remarks>
    Public Function DecompressKonamiRLE(ByVal pabytCompressed() As Byte) As List(Of Byte)
        Dim lngindex As Long = 0
        Dim lngCount As Long
        Dim bolIncrement As Boolean

        DecompressKonamiRLE = New List(Of Byte)

        Do While pabytCompressed(lngindex) <> &HFF
            bolIncrement = ((&H80 And pabytCompressed(lngindex)) = &H80)
            lngCount = &H7F And pabytCompressed(lngindex)
            lngindex += 1

            For i As Integer = 1 To lngCount
                DecompressKonamiRLE.Add(pabytCompressed(lngindex))
                If bolIncrement Then lngindex += 1
            Next i

            If Not bolIncrement Then lngindex += 1 'increment to the next byte (header)
        Loop
    End Function

    '!!!!NOT FINISHED/TESTED
    'Not currently needed.  nothing is currently editable that requires compression.
    'some testing has done performed
    Public Function CompressKonamiRLE(ByVal plbytUnCompressed As List(Of Byte)) As List(Of Byte)
        Dim lngindex As Long = 0
        Dim lngEndIndex As Long
        Dim lngCount As Long
        Dim bytInit As Byte

        CompressKonamiRLE = New List(Of Byte)

        Do
            If lngindex + 2 < plbytUnCompressed.Count Then  'At least 3 more bytes before the end
                If plbytUnCompressed(lngindex) = plbytUnCompressed(lngindex + 1) _
                    And plbytUnCompressed(lngindex) = plbytUnCompressed(lngindex + 2) Then   'At least two identical bytes
                    bytInit = plbytUnCompressed(lngindex)
                    lngCount = 0

                    Do While (plbytUnCompressed(lngindex) = bytInit) And (lngCount < &H7F)
                        lngindex += 1
                        lngCount += 1
                    Loop

                    CompressKonamiRLE.Add(lngCount)
                    CompressKonamiRLE.Add(bytInit)
                Else    'bytes are different
                    'only switched if there are now 4 identical bytes.  It costs more space to switch for 2 bytes only.
                    'Swapping for 3 identical bytes is the same space wise.  better to leave it uncompressed at that point
                    lngCount = 0
                    lngEndIndex = lngindex

                    Do While (plbytUnCompressed(lngEndIndex) <> plbytUnCompressed(lngEndIndex + 1) _
                             Or plbytUnCompressed(lngEndIndex) <> plbytUnCompressed(lngEndIndex + 2) _
                             Or plbytUnCompressed(lngEndIndex) <> plbytUnCompressed(lngEndIndex + 3)) _
                             And (lngCount < &H7F)
                        lngEndIndex += 1
                        lngCount += 1
                    Loop

                    CompressKonamiRLE.Add(&H80 Or lngCount)
                    For i As Integer = lngindex To lngEndIndex - 1
                        CompressKonamiRLE.Add(plbytUnCompressed(i))
                    Next i
                    lngindex = lngEndIndex
                End If
            Else    'one byte left

            End If
        Loop Until lngindex > plbytUnCompressed.Count

        CompressKonamiRLE.Add(&HFF) 'mark the end of the section
    End Function

    ''' <summary>
    ''' Converts one of the 64 NES colors into an RGB value
    ''' </summary>
    ''' <param name="pbytNES">The NES color index (0-63)</param>
    ''' <returns></returns>
    ''' <remarks>'These RGB values were pulled directly from www.thealmightyguru.com/Games/Hacking/Wiki/index.php/NES_Palette</remarks>
    Public Function NEStoRGB(ByVal pbytNES As Byte) As Color
        Dim abytRGB(2) As Byte

        Select Case pbytNES
            Case &H0 : abytRGB = {124, 124, 124}
            Case &H1 : abytRGB = {0, 0, 252}
            Case &H2 : abytRGB = {0, 0, 188}
            Case &H3 : abytRGB = {68, 40, 188}
            Case &H4 : abytRGB = {148, 0, 132}
            Case &H5 : abytRGB = {168, 0, 32}
            Case &H6 : abytRGB = {168, 16, 0}
            Case &H7 : abytRGB = {136, 20, 0}
            Case &H8 : abytRGB = {80, 48, 0}
            Case &H9 : abytRGB = {0, 120, 0}
            Case &HA : abytRGB = {0, 104, 0}
            Case &HB : abytRGB = {0, 88, 0}
            Case &HC : abytRGB = {0, 64, 88}
            Case &HD : abytRGB = {0, 0, 0}
            Case &HE : abytRGB = {0, 0, 0}
            Case &HF : abytRGB = {0, 0, 0}
            Case &H10 : abytRGB = {188, 188, 188}
            Case &H11 : abytRGB = {0, 120, 248}
            Case &H12 : abytRGB = {0, 88, 248}
            Case &H13 : abytRGB = {104, 68, 252}
            Case &H14 : abytRGB = {216, 0, 204}
            Case &H15 : abytRGB = {228, 0, 88}
            Case &H16 : abytRGB = {248, 56, 0}
            Case &H17 : abytRGB = {228, 92, 16}
            Case &H18 : abytRGB = {172, 124, 0}
            Case &H19 : abytRGB = {0, 184, 0}
            Case &H1A : abytRGB = {0, 168, 0}
            Case &H1B : abytRGB = {0, 168, 68}
            Case &H1C : abytRGB = {0, 136, 136}
            Case &H1D : abytRGB = {0, 0, 0}
            Case &H1E : abytRGB = {0, 0, 0}
            Case &H1F : abytRGB = {0, 0, 0}
            Case &H20 : abytRGB = {248, 248, 248}
            Case &H21 : abytRGB = {60, 188, 252}
            Case &H22 : abytRGB = {104, 136, 252}
            Case &H23 : abytRGB = {152, 120, 248}
            Case &H24 : abytRGB = {248, 120, 248}
            Case &H25 : abytRGB = {248, 88, 152}
            Case &H26 : abytRGB = {248, 120, 88}
            Case &H27 : abytRGB = {252, 160, 68}
            Case &H28 : abytRGB = {248, 184, 0}
            Case &H29 : abytRGB = {184, 248, 24}
            Case &H2A : abytRGB = {88, 216, 84}
            Case &H2B : abytRGB = {88, 248, 152}
            Case &H2C : abytRGB = {0, 232, 216}
            Case &H2D : abytRGB = {120, 120, 120}
            Case &H2E : abytRGB = {0, 0, 0}
            Case &H2F : abytRGB = {0, 0, 0}
            Case &H30 : abytRGB = {252, 252, 252}
            Case &H31 : abytRGB = {164, 228, 252}
            Case &H32 : abytRGB = {184, 184, 248}
            Case &H33 : abytRGB = {216, 184, 248}
            Case &H34 : abytRGB = {248, 184, 248}
            Case &H35 : abytRGB = {248, 164, 192}
            Case &H36 : abytRGB = {240, 208, 176}
            Case &H37 : abytRGB = {252, 224, 168}
            Case &H38 : abytRGB = {248, 216, 120}
            Case &H39 : abytRGB = {216, 248, 120}
            Case &H3A : abytRGB = {184, 248, 184}
            Case &H3B : abytRGB = {184, 248, 216}
            Case &H3C : abytRGB = {0, 252, 252}
            Case &H3D : abytRGB = {248, 216, 248}
            Case &H3E : abytRGB = {0, 0, 0}
            Case &H3F : abytRGB = {0, 0, 0}
            Case Else : abytRGB = {0, 0, 0}
        End Select

        NEStoRGB = Color.FromArgb(255, abytRGB(0), abytRGB(1), abytRGB(2))  '255 is the alpha setting (transparency)
    End Function
End Module
